package com.eddie.tenalgorithm.dynamic;

import java.util.Arrays;

/**
 * @author Eddie
 * @date 2022/08/16 09:34
 **/
public class KnapsackProblem {

    public static void main(String[] args) {
        //物品的重量
        int[] w = {1, 4, 3};
        //物品的价值,这里的value[i] 就是v[i]
        int[] value = {1500, 3000, 2000};
        //背包的容量
        int m = 4;
        //物品的个数
        int n = value.length;

        //创建二维数组
        //v[i][j] = 表示在前i个物品中能够装入容量为j的背包的最大价值
        int[][] v = new int[n + 1][m + 1];
        //为了记录商品的情况，定义一个二维数组
        int[][] path = new int[n + 1][m + 1];

        //初始化第一行第一列，这里在本程序中不处理，因为默认就为0
        for (int i = 0; i < v.length; i++) {
            //将第一列设置为0,i为行
            v[i][0] = 0;
        }
        //处理第一行设置为0
        //fill方法意为：将指定的 int 值分配给指定的 int 数组的每个元素。
        Arrays.fill(v[0], 0);

        //根据前面得到的公式来动态规划处理
        /*
        说明：1、i=1是因为第一行默认为0直接越过
             2、j=1是因为第一列也默认为0直接越过，不处理第一列
         */
        for (int i = 1; i < v.length; i++) {
            for (int j = 1; j < v[i].length; j++) {
                //公式
                //如果当前商品的容量大于当前背包的容量,就直接将上一行该列购买的商品放入到当前格
                //因为i从1开始，因此这里要-1去获取对应的商品的重量
                if (w[i - 1] > j) {
                    //将当前背包的的购买格放入上一件商品同样同一列格子的商品价格
                    v[i][j] = v[i - 1][j];
                } else { //如果背包容量大于等于商品的容量
                    /*
                    说明：
                    1、v[i][j] = 当前背包格
                    2、v[i-1][j] = 等于上一轮商品的当前列的背包格商品价值
                    3、value[i-1] + v[i-1][j - w[i-1]]
                       value[i-1] 意为当前准备添加的商品的价格
                       v[i-1][j - w[i-1]] 意为 上一轮商品中 容量为 当前背包容量减去当前要添加的商品的容量之后对应背包格的商品价格
                    4、通过以上获取之后，使用Math.max方法获取上一轮商品的价格与当前商品价格加上购买后剩余容量能够上一轮添加的商品的价格
                       中的最大值去选择
                     */
//                    v[i][j] = Math.max(v[i - 1][j], value[i - 1] + v[i - 1][j - w[i - 1]]);
                    //为了记录商品存放到背包的情况，不能直接使用上面的公式，需要if else来体现该公式

                    //如果上一轮商品价格小于当前商品的价格加购买当前商品后剩余容量在上一轮中可以选择的商品的价格,就直接选择后者添加
                    if (v[i - 1][j] < value[i - 1] + v[i - 1][j - w[i - 1]]) {
                        v[i][j] = value[i - 1] + v[i - 1][j - w[i - 1]];
                        //把当前的情况记录到path
                        path[i][j] = 1;
                    } else {
                        //如果上一轮的商品价格高就直接放入
                        v[i][j] = v[i - 1][j];
                    }
                }
            }
        }

        //输出一下v看情况
        for (int[] ints : v) {
            for (int anInt : ints) {
                System.out.print(anInt + " ");
            }
            System.out.println();
        }

        /*
        输出放入了哪些商品
        遍历path，这样输出会把所有放入情况都得到，其实只需要最后的放入情况
                for (int[] ints : path) {
                    for (int anInt : ints) {
                        if (anInt == 1) {
                            System.out.println("第"+anInt+"个商品存入到背包");
                        }
                    }
                }
        */

        //行的最大下标
        int i = path.length - 1;
        //列的最大下标
        int j = path[0].length - 1;
        //从path最后开始找
        while (i > 0 && j > 0) {
            //说明这个点放置过物品
            if (path[i][j] == 1) {
                System.out.println("第" + path[i][j] + "个商品存入到背包");
                j -= w[i - 1];
            }
            i--;
        }

    }

}
